package com.craftworks.rtmp.amf0;

import com.craftworks.util.BigEndian;

public final class AMF0
{
    private AMF0() {}

    public static int getDataTypeSize(byte[] bytes, int offset)
    {
        int type = BigEndian.getUINT8(bytes, offset);
        switch (type)
        {
            case 0x00: // Number type
                return 9; // 1 + 8
            case 0x01: // Boolean type
                return 2; // 1 + 1
            case 0x02: // String type
                return 3 + BigEndian.getUINT16(bytes, offset + 1);
            case 0x03: // Object type
                return 1 + getObjectPropertiesLength(bytes, offset + 1);
            case 0x05: // Null type
            case 0x06: // Undefined type
            case 0x0D: // Unsupported type
                return 1;
            case 0x07: // Reference type
                return 3; // 1 + 2
            case 0x08: // ECMA Array type
                return 5 + getObjectPropertiesLength(bytes, offset + 5);
            case 0x09: // Object end type
                return 3;
            case 0x0A: // Strict Array type
                return (int) (1 + 4 + BigEndian.getUINT32(bytes, offset + 1));
            case 0x0B: // Date type
                return 1 + 8 + 2;
            case 0x0C: // Long String type
            case 0x0F: // XML Document type
            {
                int strLen = (int) (4 + BigEndian.getUINT32(bytes, offset + 1));
                return 1 + strLen;
            }
            case 0x10: // Typed Object type
            {
                int strLen = 2 + BigEndian.getUINT16(bytes, offset + 1);
                return 1 + strLen + getObjectPropertiesLength(bytes, offset + 1 + strLen);
            }
            default:
                throw new UnsupportedOperationException("unsupported type: " + type);
        }
    }

    public static int getPropertyLength(byte[] bytes, int offset)
    {
        int nameLen = BigEndian.getUINT16(bytes, offset);
        offset += 2 + nameLen;
        return 2 + nameLen + getDataTypeSize(bytes, offset);
    }

    public static boolean isObjectEndProperty(byte[] bytes, int offset)
    {
        int u24 = BigEndian.getUINT24(bytes, offset);
        return u24 == 0x000009;
    }

    public static boolean isNotObjectEndProperty(byte[] bytes, int offset)
    {
        int u24 = BigEndian.getUINT24(bytes, offset);
        return u24 != 0x000009;
    }

    public static int getObjectPropertiesLength(byte[] bytes, int offset)
    {
        int size = 0;
        while (isNotObjectEndProperty(bytes, offset))
        {
            int propertySize = getPropertyLength(bytes, offset);
            size += propertySize;
            offset += propertySize;
        }
        return size + 3;
    }

    public static DataType decode(byte[] bytes, int offset)
    {
        int type = BigEndian.getUINT8(bytes, offset);
        switch (type)
        {
            case 0x00: // Number type
                return new NumberType(bytes, offset);
            case 0x01: // Boolean type
                return new BooleanType(bytes, offset);
            case 0x02: // String type
                return new StringType(bytes, offset);
            case 0x03: // Object type
                return new ObjectType(bytes, offset);
            case 0x05: // Null type
                return new NullType(bytes, offset);
            case 0x06: // Undefined type
                return new UndefinedType(bytes, offset);
            case 0x0D: // Unsupported type
                return new UnsupportedType(bytes, offset);
            default:
                return null;
        }
    }
}
